package cn.swust.designpatternlearningplatform.domain.bo;

import cn.swust.designpatternlearningplatform.common.AnnotationUtils.NotNull;
import cn.swust.designpatternlearningplatform.common.exception.CustomException;
import lombok.Getter;
import lombok.ToString;


import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 该类用于智能组卷
 * 主要用在service层内，包装题目的类型和id
 * 以下类型为List的统称为题目列表
 * 题目列表为空的代表没有这种类型的题目
 *
 * @author Tang
 * @date 2023/5/14 21:57
 **/
@ToString
@Getter
public class PaperBo implements Cloneable {
    /**
     * 用于记录该试卷用哪几种类型的题目
     * 用于记录四种题目的数量
     *
     * @author Tang
     * @date 2023/5/14 22:13
     **/
    private final Integer[] nums;
    /**
     * 判断题（存取判断题的id）
     *
     * @author Tang
     * @date 2023/5/14 22:10
     **/
    private List<Integer> judgeList;
    /**
     * 选择题（默认可存取单选题题id和多选题id，若multipleChoiceList被使用，
     * 则choiceList不能选择多项选择）
     *
     * @author Tang
     * @date 2023/5/14 21:59
     **/
    private List<Integer> choiceList;
    /**
     * 多项选择
     * （只能存取多项选择题id，如果multipleChoiceList被使用，
     * 则choice不能被用作存取多项选择）
     *
     * @author Tang
     * @date 2023/5/14 22:01
     **/
    private List<Integer> multipleChoiceList;
    /**
     * 简答题（只用于存取简答题id）
     *
     * @author Tang
     * @date 2023/5/14 22:08
     **/
    private List<Integer> shortAnswerQuestionList;
    /**
     * 试卷适应度
     *
     * @author Tang
     * @date 2023/5/16 23:33
     **/
    private BigDecimal fitness;

    /**
     * 初始化试卷种群
     *
     * @return
     * @author Tang
     * @date 2023/5/15 10:25
     **/
    public PaperBo(QuestionTypes types) {
        this.nums = types.getRecord();
    }

    public void setJudgeList(List<Integer> judgeList) {
        this.judgeList = new ArrayList<>(judgeList.subList(0, this.nums[0]));
    }

    public void setChoiceList(List<Integer> choiceList) {
        this.choiceList = new ArrayList<>(choiceList.subList(0, this.nums[1]));
    }

    public void setMultipleChoiceList(List<Integer> multipleChoiceList) {
        this.multipleChoiceList = new ArrayList<>(multipleChoiceList.subList(0, this.nums[2]));
    }

    public void setShortAnswerQuestionList(List<Integer> shortAnswerQuestionList) {
        this.shortAnswerQuestionList = new ArrayList<>(shortAnswerQuestionList.subList(0, this.nums[3]));
    }

    public void setFitness(BigDecimal fitness) {
        this.fitness = fitness;
    }

    /**
     * 获取试卷中所有题目的id
     *
     * @return java.util.List<java.lang.Integer>
     * @author Tang
     * @date 2023/5/17 10:21
     **/
    public List<List<Integer>> getAllTopic() {
        List<List<Integer>> all = new ArrayList<>(4);
        all.add(0, this.judgeList);
        all.add(1, this.choiceList);
        all.add(2, this.multipleChoiceList);
        all.add(3, this.shortAnswerQuestionList);
        return all;
    }

    /**
     * PaperBo的克隆方法
     *
     * @param
     * @return cn.swust.designpatternlearningplatform.domain.bo.PaperBo
     * @author Tang
     * @date 2023/5/15 10:56
     **/
    @Override
    public PaperBo clone() {
        try {
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return (PaperBo) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PaperBo)) return false;
        PaperBo paperBo = (PaperBo) o;
        return Arrays.equals(getNums(), paperBo.getNums())
                && Objects.equals(getJudgeList(), paperBo.getJudgeList())
                && Objects.equals(getChoiceList(), paperBo.getChoiceList())
                && Objects.equals(getMultipleChoiceList(), paperBo.getMultipleChoiceList())
                && Objects.equals(getShortAnswerQuestionList(), paperBo.getShortAnswerQuestionList())
                && Objects.equals(getFitness(), paperBo.getFitness());
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(getJudgeList(), getChoiceList(), getMultipleChoiceList(), getShortAnswerQuestionList(), getFitness());
        result = 31 * result + Arrays.hashCode(getNums());
        return result;
    }

    /**
     * 获取初始的种群数（每套试卷为一个种群）
     *
     * @param num
     * @param types
     * @param random 使用本地线程安全的随机数生成器
     * @return java.util.List<cn.swust.designpatternlearningplatform.domain.bo.PaperBo>
     * @author Tang
     * @date 2023/5/20 8:52
     **/
    public static List<PaperBo> getInitialPopulation(Integer num, QuestionTypes types, ThreadLocalRandom random) {
        //试卷套数（种群数目）
        List<PaperBo> population = new ArrayList<>(2 * num);
        PaperBo parent = new PaperBo(types);
        for (int i = 0; i < num; i++) {
            //打乱题目id
            Collections.shuffle(types.getJudgeListTotal(), random);
            Collections.shuffle(types.getChoiceListTotal(), random);
            Collections.shuffle(types.getMultipleChoiceListTotal(), random);
            Collections.shuffle(types.getShortAnswerQuestionListTotal(), random);
            //克隆新的试卷
            PaperBo son = parent.clone();
            son.setJudgeList(types.getJudgeListTotal());
            son.setChoiceList(types.getChoiceListTotal());
            son.setMultipleChoiceList(types.getMultipleChoiceListTotal());
            son.setShortAnswerQuestionList(types.getShortAnswerQuestionListTotal());
            population.add(son);
        }
        return population;
    }

    /**
     * 试卷各题型参数
     *
     * @author Tang
     * @date 2023/5/15 10:28
     **/
    @ToString
    static public class QuestionTypes {
        private final Integer[] record = new Integer[4];
        @Getter
        private final List<Integer> judgeListTotal = new ArrayList<>();
        @Getter
        private final List<Integer> choiceListTotal = new ArrayList<>();
        @Getter
        private final List<Integer> multipleChoiceListTotal = new ArrayList<>();
        @Getter
        private final List<Integer> shortAnswerQuestionListTotal = new ArrayList<>();

        /**
         * 设置判断题的数目和题库
         *
         * @param judgeNum
         * @return void
         * @author Tang
         * @date 2023/5/15 10:33
         **/
        public void setJudge(@NotNull List<Integer> judgeListTotal, Integer judgeNum) {
            this.judgeListTotal.addAll(judgeListTotal);
            if (judgeNum > this.getJudgeListTotal().size()) {
                throw new CustomException("判断题数目过大，仅有" + this.getJudgeListTotal().size() + "道题");
            }
            this.record[0] = judgeNum;
        }

        /**
         * 设置选择题的数目和题库
         *
         * @param choiceNum
         * @return void
         * @author Tang
         * @date 2023/5/15 10:33
         **/
        public void setChoice(@NotNull List<Integer> choiceListTotal, Integer choiceNum) {
            this.choiceListTotal.addAll(choiceListTotal);
            if (choiceNum > this.getChoiceListTotal().size()) {
                throw new CustomException("单选题数目过大，仅有" + this.getChoiceListTotal().size() + "道题");
            }
            this.record[1] = choiceNum;
        }

        /**
         * 设置多选题的数目和题库
         *
         * @param multipleChoiceNum
         * @return void
         * @author Tang
         * @date 2023/5/15 10:33
         **/
        public void setMultipleChoice(@NotNull List<Integer> multipleChoiceListTotal, Integer multipleChoiceNum) {
            this.multipleChoiceListTotal.addAll(multipleChoiceListTotal);
            if (multipleChoiceNum > this.getMultipleChoiceListTotal().size()) {
                throw new CustomException("多选题数目过大，仅有" + this.getMultipleChoiceListTotal().size() + "道题");
            }
            this.record[2] = multipleChoiceNum;
        }

        /**
         * 设置简答题的数目和题库
         *
         * @param shortAnswerQuestionNum
         * @return void
         * @author Tang
         * @date 2023/5/15 10:33
         **/
        public void setShortAnswerQuestion(@NotNull List<Integer> shortAnswerQuestionListTotal, Integer shortAnswerQuestionNum) {
            this.shortAnswerQuestionListTotal.addAll(shortAnswerQuestionListTotal);
            if (shortAnswerQuestionNum > this.getShortAnswerQuestionListTotal().size()) {
                throw new CustomException("简答题数目过大，仅有" + this.getShortAnswerQuestionListTotal().size() + "道题");
            }
            this.record[3] = shortAnswerQuestionNum;
        }

        /**
         * 获取各题型的数目
         *
         * @param
         * @return java.lang.Integer[]
         * @author Tang
         * @date 2023/5/15 10:34
         **/
        public Integer[] getRecord() {
            return record;
        }

        /**
         * 获取总题库id
         *
         * @return java.util.List<java.lang.Integer>
         * @author Tang
         * @date 2023/5/17 0:10
         **/
        public List<Integer> getAllTopicIds() {
            List<Integer> all = new ArrayList<>();
            all.addAll(this.judgeListTotal);
            all.addAll(this.choiceListTotal);
            all.addAll(this.multipleChoiceListTotal);
            all.addAll(this.shortAnswerQuestionListTotal);
            return all;
        }

        /**
         * 获取总体库
         *
         * @return java.util.List<java.util.List < java.lang.Integer>>
         * @author Tang
         * @date 2023/5/22 12:08
         **/
        public List<List<Integer>> getAllTopic() {
            List<List<Integer>> all = new ArrayList<>(4);
            all.add(0, this.judgeListTotal);
            all.add(1, this.choiceListTotal);
            all.add(2, this.multipleChoiceListTotal);
            all.add(3, this.shortAnswerQuestionListTotal);
            return all;
        }

        public QuestionTypes() {
            Arrays.fill(this.record, 0);
        }
    }
}
